/*
 * interface.cpp
 *
 *  Created on: Jun 8, 2009
 *      Author: andrei
 */



#include <stdio.h>
#include <string>
#include <iostream>
//#include <GLUT/glut.h>	// Mac OS X
#include <GL/glut.h>	// Linux //faut installer le package freeglut3-dev et les autres
#include <GL/gl.h>
#include <stdlib.h>
//#include "stdafx.h"
//#include <math.h>

using namespace std;

// Global Parameters
#define UNIT	0.5
#define	AXES	1.0
#define ESCAPE	27

// GLUT window
GLint window;
GLint x_size = 320;
GLint y_size = 480;

// Mouse
GLboolean left_btn_down = false;
GLboolean mouse_in = false;
GLint mouse_x;
GLint mouse_y;

// Full screen
GLboolean fullscreen = false;
GLint pref_Xpos;
GLint pref_Ypos;
GLint pref_Xsize;
GLint pref_Ysize;

// Display
GLboolean disp_axes = true;
GLboolean disp_help = false;

// Debug
GLboolean debug = true;
GLboolean debug_msg = true;
GLboolean disp_monitor = true;

// Square position
double Xpos;
double Ypos;
double Zpos;


// PROTOTYPES //////////////////////////////////////////////////////////////////

GLvoid drawScene();
GLvoid drawAxes();
GLvoid drawText(GLint x, GLint y, char* s, GLfloat r, GLfloat g, GLfloat b);
GLvoid drawQuad();


// MAINTENANCE FUNCTIONS ///////////////////////////////////////////////////////


/**
 * GLvoid transform
 * Called for each scene re-rendering.
 */
GLvoid transform(GLfloat width, GLfloat height) {
	// Set the viewport
	glViewport(0, 0, width, height);
	// Select the projection matrix
	glMatrixMode(GL_PROJECTION);
	// Reset the projection matrix
	glLoadIdentity();
	// Calculate the aspect ratio of the window
	gluPerspective(45.0, width/height, 0.1, 100.0);
	// Switch back to the model view matrix
	glMatrixMode(GL_MODELVIEW);
}

/**
 * GLvoid initGL
 * General OpenGL initialisation function.
 * Sets all of the initial parameters.
 */
GLvoid initGL(GLfloat width, GLfloat height) {
	// Black background
	glClearColor(0.0, 0.0, 0.0, 1.0);
	// Point size
	glPointSize(1.0);
	// Line width
	glLineWidth(1.0);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	// Perform the transformation
	transform(width, height);
}


// CALLBACKS FUNCTIONS /////////////////////////////////////////////////////////

/**
 * GLvoid reshape
 * Called when the window is resized.
 */
GLvoid reshape(GLint width, GLint height) {
	// Sanity checks
	if (height == 0)	height = 1;
	if (width == 0)		width = 1;
	// Perform the transformation
	transform(width, height);
}

/**
 * GLvoid display
 */
GLvoid display() {
	// Clear screen and depth buffers
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	// Console
	printf("(display) drawing called.\n");

	// Camera position
	glLoadIdentity();
	gluLookAt(0.0, 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

	// Call the main drawing function
	drawScene();

	glFlush();
	glutSwapBuffers();
}

/**
 * GLvoid keyboard
 * Keyboard handling function.
 */
GLvoid keyboard(GLubyte key, GLint x, GLint y) {

    switch (key) {
		case ESCAPE:
			printf("(keyboard) ESCAPE pressed. Exit.\n");
			glutDestroyWindow(window);	// Kill window
			exit(0);					// Very dirty exit
			break;						// Do we need this ?

		case 'a':						// Axes
			disp_axes = !disp_axes;
			break;

		case 'b':						// Transparency
			if (!glIsEnabled(GL_BLEND)) {
				glEnable(GL_BLEND);		// Turn blending ON
				//glDisable(GL_DEPTH_TEST);	// Turn depth testing OFF
			} else {
				glDisable(GL_BLEND);	// Turn blending OFF
				//glEnable(GL_DEPTH_TEST);	// Turn depth testing ON
			}
			break;

		case 'm':						// Monitor
			disp_monitor = !disp_monitor;
			break;

		default:
			break;
    }

	glutPostRedisplay();
}

/**
 * GLvoid special
 * Called when a special key is pressed.
 */
GLvoid special(int key, int x, int y) {

	switch (key) {

		case GLUT_KEY_F1:
			disp_help = !disp_help;
			break;

		case GLUT_KEY_F2:
			fullscreen = !fullscreen;
			if (fullscreen) {
				// Save parameters
				pref_Xpos  = glutGet((GLenum) GLUT_WINDOW_X);
				pref_Ypos  = glutGet((GLenum) GLUT_WINDOW_Y);
				pref_Xsize = glutGet((GLenum) GLUT_WINDOW_WIDTH);
				pref_Ysize = glutGet((GLenum) GLUT_WINDOW_HEIGHT);
				// Go to full screen
				glutFullScreen();
			} else { // Restore
				glutReshapeWindow(pref_Xsize, pref_Ysize);
				glutPositionWindow(pref_Xpos, pref_Ypos);
			}
			break;

		default:
			break;
	}

	glutPostRedisplay();
}

/**
 * GLvoid mouse
 * Mouse handling function.
 */
GLvoid mouse(int btn, int state, int x, int y) {

	switch (btn) {

		case GLUT_LEFT_BUTTON:
			if (state == GLUT_DOWN)	left_btn_down = true;
			else					left_btn_down = false;
			break;

		default:
			break;
	}
}

/**
 * GLvoid motion
 * Motion.
 */
GLvoid motion (int x, int y) {

	mouse_x = x;
	mouse_y = y;
	printf("(motion) Mouse position: (%d, %d)\n", mouse_x, mouse_y);
	/*
	double modelview[16], projection[16];
    int viewport[4];
    float z;

	//get the projection matrix
    glGetDoublev(GL_PROJECTION_MATRIX, projection);
	//get the modelview matrix
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
	//get the viewport
    glGetIntegerv(GL_VIEWPORT, viewport);

    //Read the window z co-ordinate (the z value on that point in unit cube)
    glReadPixels(x, viewport[3]-y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z);

    //Unproject the window co-ordinates to find the world co-ordinates.
    gluUnProject(x, viewport[3]-y, 0.99, modelview,
				 projection, viewport, &Xpos, &Ypos, &Zpos);
	*/
	glutPostRedisplay();
}

/**
 * GLvoid passiveMotion
 * Passive Motion.
 */
GLvoid passiveMotion (int x, int y) {

	mouse_x = x;
	mouse_y = y;
	printf("(passive motion) Mouse position: (%d, %d)\n", mouse_x, mouse_y);

	double modelview[16], projection[16];
    int viewport[4];
    float z;

	//get the projection matrix
    glGetDoublev(GL_PROJECTION_MATRIX, projection);
	//get the modelview matrix
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
	//get the viewport
    glGetIntegerv(GL_VIEWPORT, viewport);

    //Read the window z co-ordinate (the z value on that point in unit cube)
    glReadPixels(x, viewport[3]-y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z);

    //Unproject the window co-ordinates to find the world co-ordinates.
    gluUnProject(x, viewport[3]-y, 0.99, modelview,
				 projection, viewport, &Xpos, &Ypos, &Zpos);

	glutPostRedisplay();
}

/**
 * GLvoid entry
 * Mouse entry.
 */
void entry(int state) {

	switch (state) {

		case GLUT_ENTERED:
			printf("(entry) Entered window %d\n", window);
			mouse_in = true;
			break;
		case GLUT_LEFT:
			printf("(entry) Left window %d\n", window);
			mouse_in = false;
			break;

		default:
			break;
	}

	glutPostRedisplay();
}


// DRAWING FUNCTIONS ///////////////////////////////////////////////////////////

/**
 * GLvoid drawScene
 * Main drawing function.
 * In here we put all the OpenGL and calls to routines which manipulate
 * the OpenGL state and environment.
 * Called when a redisplay is requested.
 */
GLvoid drawScene() {

	// Clear Screen and Depth Buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Axes
	if (disp_axes) drawAxes();

	// Square
	glPushMatrix();
		glColor3f(1.0, 1.0, 0.0);
		glTranslatef(Xpos, Ypos, Zpos);
		drawQuad();
    glPopMatrix();

	// Help
	if (disp_help) {
		GLint width	= glutGet((GLenum) GLUT_WINDOW_WIDTH);
		GLint height = glutGet((GLenum) GLUT_WINDOW_HEIGHT);
		string help;
		help += "Help (F1)\n";
		help += "Monitor (m)\n";
		help += "Full screen (F2)\n";
		help += "Axes (a)\n";
		help += "Blend (b)\n";
		help += "Quit (ESCAPE)\n";

		char text[100];
		for(size_t i = 0; i < help.size(); i++) {
			text[i] = help[i];
		}
		drawText(width-100, height-100, text, 1.0, 1.0, 1.0);
	}

	// Text
	if (disp_monitor) {
		char text[50];
		if (mouse_x >= 0 && mouse_x <= glutGet((GLenum) GLUT_WINDOW_WIDTH)
			&& mouse_y >= 0 && mouse_y <= glutGet((GLenum) GLUT_WINDOW_HEIGHT))
			sprintf(text, "Mouse position: (%d, %d)", mouse_x, mouse_y);
		else
			sprintf(text, "Mouse position: out.", mouse_x, mouse_y);
		drawText(4, 4, text, 1.0, 1.0, 1.0);
	}
}

/**
 * GLvoid drawAxes
 * Axes drawing.
 */
GLvoid drawAxes() {

	glBegin(GL_LINES);
		glColor3f(0.0, 1.0, 0.0);
		glVertex3f(-AXES,   0.0,   0.0);
		glVertex3f(+AXES,   0.0,   0.0);
		glVertex3f(  0.0, -AXES,   0.0);
		glVertex3f(  0.0, +AXES,   0.0);
		glVertex3f(  0.0,   0.0, -AXES);
		glVertex3f(  0.0,   0.0, +AXES);
	glEnd();

}

/**
 * GLvoid drawText
 * Text drawing function.
 */
GLvoid drawText(GLint x, GLint y, char* s, GLfloat r, GLfloat g, GLfloat b) {

    int lines;
    char* p;

    glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glOrtho(0.0, glutGet(GLUT_WINDOW_WIDTH),
			0.0, glutGet(GLUT_WINDOW_HEIGHT), -1.0, 1.0);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	glColor3f(r,g,b);
	glRasterPos2i(x, y);
	for(p = s, lines = 0; *p; p++) {
		if (*p == '\n') {
			lines++;
			glRasterPos2i(x, y-(lines*18));
		}
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_10, *p);
	}
	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
}

/**
 * GLvoid drawQuad
 * Draw a quadrilateral.
 */
GLvoid drawQuad() {

	glBegin(GL_QUADS);						// Draw A Quadrilateral
		glColor4f(1.0, 0.0, 0.0, 0.4);		// Color RGBA
		glVertex3f(-UNIT,  UNIT, -1.0f);	// Top Left
		glVertex3f( UNIT,  UNIT, -1.0f);	// Top Right
		glVertex3f( UNIT, -UNIT, -1.0f);	// Bottom Right
		glVertex3f(-UNIT, -UNIT, -1.0f);	// Bottom Left
	glEnd();
}



// MAIN ////////////////////////////////////////////////////////////////////////

//int main(int argc, char **argv)
void interface()
{

	// Initialisation and window creation
	glutInit();					// Initialise GLUT stage

	glutInitDisplayMode(GLUT_RGBA |			// RGB and Alpha
						GLUT_DOUBLE |		// Double buffer
						GLUT_DEPTH);		// Z buffer (depth)

	// Window
	glutInitWindowSize(y_size, x_size);		// Initial window size
	glutInitWindowPosition(0,0);			// Upper left corner of the screen

	// Open a window with a title
	window = glutCreateWindow("Symbios.test");
	initGL(y_size, x_size);					// Window initialisation

	// Callbacks registrations
	glutDisplayFunc(display);				// OpenGL drawing
	glutReshapeFunc(reshape);				// Reshape
	glutKeyboardFunc(keyboard);				// Keyboard handling function
	glutSpecialFunc(special);				// Special key function
	glutMouseFunc(mouse);					// Mouse handling function
	glutMotionFunc(motion);					// Motion function
	glutPassiveMotionFunc(passiveMotion);	// Passive Motion function
	glutEntryFunc(entry);					// Mouse entry


	// Event loop
	glutMainLoop();							// Start Event Processing Engine

	return 1;
}
// END

